_client=(function(client) {
  var url = 'ws://'+document.location.host+'/game';
  var ws = null;
  var callbacks = {};
  var services = [];
  
  var generate_id = function() {
    return (Math.random() + '_' + Date.now()).substring(2);
  };
  
  var invoke = client.invoke = function(action, data) {
    var dfd = $.Deferred();
    if (typeof data === 'undefined') {
      data = {}
    } else {
      if (typeof data !== 'object') {
        dfd.reject('error');
        return dfd.promise();
      }
    }
    if (!Boolean(ws)) {
      dfd.reject('error');
      return dfd.promise();
    }
    var req = {action:action, data:data};
    var id = req.id = generate_id();
    req = JSON.stringify(req);
    callbacks[id] = {
      callback:function(obj) {
        if (obj.result == 'ok') {
          dfd.resolve(obj.data);
        } else {
          dfd.reject(obj.error); 
        }
      }
    };
    console.log(req);
    ws.send(req);
    
    return dfd.promise();
  };

  client.create_service = function(type) {
    var service = services[type];
    if (typeof service === 'undefined') {
      service = services[type] = {};
      services.push(service);
      service.client = client;
      service.type = type;
    }
    
    service.enter_room = function(room_id) {
      return client.invoke('player_enter_room', {type: type, room: room_id});
    }

    return service;
  };
  
  
  client.get_room_info = function() {
    return client.invoke('room_info');
  };
  
  client.update_name = function(name) {
    return client.invoke('player_set_profile', {name: name}).then(function(data) {
      client.name = name;
      return data;
    });
  };
  
  client.connect = function() {
    if (ws !== null) {
      return;
    }
    ws = new WebSocket(url);
    ws.onopen = function() {
      client.invoke('player_get_profile').done(function(data){
        client.id = data.id;
        client.name = data.name;
        document.cookie='player=' + client.id;
        client.onconnect && client.onconnect();
      }).fail(function(){
        client.close();
      })
    };
    ws.onmessage = function(evt) {
      console.log(evt.data);
      var obj = JSON.parse(evt.data);
      if (typeof obj.id === 'string') {
        var cb = callbacks[obj.id];
        delete callbacks[obj.id]
        if (Boolean(cb)) {
          cb.callback(obj);
          return;
        }
      }
      
      var handle_name = '_handle_' + obj.action;
      var handler = client[handle_name];
      if (typeof handler !== 'undefined') { 
        handler(obj.data);
      }
      services.forEach(function(service, index){
        var handler = service[handle_name];
        if (typeof handler !== 'undefined') { 
          handler(obj.data);
        }
      });
    };
    ws.onclose = function() {
      client.onclose && client.onclose();
      ws = null;
    };
  };

  client.close = function() {
    ws && ws.close();
  };
  
  return client;
})(window._client || {});